import { useCallback, useReducer, useState } from "react"

const UNDO = 'UNDO';
const REDO = 'REDO';
const SET = 'SET';
const RESET = 'RESET';

type State<T> = {
    past:T[],
    future:T[],
    present:T
}

type Action<T> = {
    type:typeof UNDO | typeof REDO | typeof SET | typeof RESET,
    newPresent?:T
}

const undoReducer = <T>(state:State<T>,action:Action<T>)=>{

    const {past,future,present} = state;
    const {type,newPresent} = action;
    switch(type){
        case UNDO:{
            if(past.length<=0){
                return state;
            }
            const lastPast = past[past.length-1];
            return {
                past: past.slice(0,past.length-1),
                present:lastPast,
                future:[lastPast,...future]
            };
        }
        case REDO:{
            if(future.length<=0){
                return state;
            }
            const nextFuture = future[0];
            return {
                past: [...past,nextFuture],
                present:nextFuture,
                future:future.slice(1)
            };
        }
        case SET:{
            if(newPresent === present){
                return state;
            }
            return {
                past:[...past,present],
                present:newPresent,
                future:[]
            }
        }
        case RESET:{
            return {
                past:[],
                present:newPresent,
                future:[]
            };
        }
        default:{
            return state;
        }
    }
}


export const useUndo=<T>(initalPresent:T)=>{
    const [state,dispatch] = useReducer(undoReducer,{
        past:[],
        future:[],
        present:initalPresent
    } as State<T>);

    const canUndo = state.past.length > 0;
    const canRedo = state.future.length > 0;

    const set = useCallback((newPresent:T)=>{dispatch({type:SET,newPresent});},[])

    const undo = useCallback(()=>{dispatch({type:UNDO});},[]);

    const redo = useCallback(()=>{dispatch({type:REDO});},[]);

    const reset = useCallback((newPresent:T)=>{dispatch({type:RESET,newPresent});},[]);

    return [
        state,
        {set,reset,undo,redo,canUndo,canRedo}
    ] as const;
}